﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using HWND = System.IntPtr;  

namespace JsRunTool.Ctl
{
    public partial class SyntaxEditor : System.Windows.Forms.RichTextBox
    {
        /// <summary>   
        /// 必需的设计器变量。  

        /// </summary>  
        //private System.ComponentModel.Container components = null;
        private System.Windows.Forms.RichTextBox _LineId;
        int line;
        private Parser parser;
        /// <summary>  
        /// 必需的设计器变量。  
        /// </summary>  
        //使用win32api：SendMessage来防止控件着色时的闪烁现象  
        [DllImport("user32")]
        private static extern int SendMessage(HWND hwnd, int wMsg, int wParam, IntPtr lParam);
        private const int WM_SETREDRAW = 0xB;
        public SyntaxEditor()
        {
            // 该调用是 Windows.Forms 窗体设计器所必需的。  
            InitializeComponent();
            base.WordWrap = false;
            // TODO: 在 InitComponent 调用后添加任何初始化  
            {

            }
        }
        public void init()
        {
            _LineId = new System.Windows.Forms.RichTextBox();
            //_LineId.Name = "LineId";
            _LineId.Text = "1";
            _LineId.Width = 40;
            _LineId.Height = base.ClientSize.Height;
            _LineId.Dock = base.Dock;
            _LineId.Font = this.Font;
            _LineId.ForeColor = Color.Green;
            _LineId.ReadOnly = true;
            _LineId.BackColor = Color.White;
            _LineId.TabStop = false;
            //_LineId.BorderStyle = this.BorderStyle;
            _LineId.ScrollBars = this.ScrollBars;
            //_LineId.ScrollBars = RichTextBoxScrollBars.None;
            //_LineId.Cursor = Cursors.Arrow;
            _LineId.Cursor = this.Cursor;
            this.SelectionIndent = 40;//由于左边行号占用了40象素，所以AdaScrollRichTextBox 需要向右边缩进40象素。
            this.Controls.Add(_LineId);//注意这里，将_LineId添加进AdaScrollRichTextBox Controls。
        }
        
        public void showAll()
        {
            int lines = base.Lines.Length;
            int selectStart = base.SelectionStart;
            line = base.GetLineFromCharIndex(selectStart);
            if (line == 0 && lines > 0)
            {
                for (; line < lines; line++)
                {
                    string lineStr = base.Lines[line];
                    int linestart = 0;
                    for (int i = 0; i < line; i++)
                    {
                        linestart += base.Lines[i].Length + 1;
                    }

                    SendMessage(base.Handle, WM_SETREDRAW, 0, IntPtr.Zero);

                    base.SelectionStart = linestart;
                    base.SelectionLength = lineStr.Length;
                    base.SelectionColor = Color.Black;
                    base.SelectionLength = 0;

                    //对一行字符串用空格或者.分开  
                    string[] words = lineStr.Split(new char[] { ' ', '.', '\n', '(', ')', '}', '{', '"', '[', ']' });
                    parser = new Parser(this.language);

                    for (int i = 0; i < words.Length; i++)//一个字符段一个字符段来判断  
                    {
                        //判断是否是程序保留字 是的话高亮显示  
                        if (parser.IsKeyWord(words[i]) != Color.Black)
                        {
                            int length = 0;
                            for (int j = 0; j < i; j++)
                            {
                                length += words[j].Length;
                            }
                            length += i;
                            int index = lineStr.IndexOf(words[i], length);

                            base.SelectionStart = linestart + index;
                            base.SelectionLength = words[i].Length;
                            //base.SelectionFont  
                            base.SelectionColor = parser.IsKeyWord(words[i]);
                            base.SelectionStart = selectStart;
                            base.SelectionLength = 0;
                        }
                    }
                    SendMessage(base.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
                    base.Refresh();
                }
            }
        }
        /// <summary>  
        /// 清理所有正在使用的资源。  
        /// </summary>  

        #region 组件设计器生成的代码
        /// <summary>  
        /// 设计器支持所需的方法 - 不要使用代码编辑器   
        /// 修改此方法的内容。  
        /// </summary>  

        #endregion
        //重写基类的OnTextChanged方法。为了提高效率，程序是对当前文本插入点所在行进行扫描，  
        //以空格为分割符，判断每个单词是否为关键字，并进行着色。  
        protected override void OnTextChanged(EventArgs e)
        {
            if (base.Text != "")
            {
                int lines = base.Lines.Length;

                int selectStart = base.SelectionStart;
                line = base.GetLineFromCharIndex(selectStart);
//                 if (line == 0 && lines > 0)
//                 {
//                    // showAll();
//                     base.OnTextChanged(e);
//                     return;
//                 }
 
                string lineStr = base.Lines[line];
                int linestart = 0;
                for (int i = 0; i < line; i++)
                {
                    linestart += base.Lines[i].Length + 1;
                }

                SendMessage(base.Handle, WM_SETREDRAW, 0, IntPtr.Zero);

                base.SelectionStart = linestart;
                base.SelectionLength = lineStr.Length;
                base.SelectionColor = Color.Black;
                base.SelectionStart = selectStart;
                base.SelectionLength = 0;
 
                //对一行字符串用空格或者.分开  
                string[] words = lineStr.Split(new char[] { ' ', '.', '\n', '(', ')', '}', '{', '"', '[', ']' });
                parser = new Parser(this.language);

                for (int i = 0; i < words.Length; i++)//一个字符段一个字符段来判断  
                {

                    //判断是否是程序保留字 是的话高亮显示  
                    if (parser.IsKeyWord(words[i]) != Color.Black)
                    {
                        int length = 0;
                        for (int j = 0; j < i; j++)
                        {
                            length += words[j].Length;
                        }
                        length += i;
                        int index = lineStr.IndexOf(words[i], length);

                        base.SelectionStart = linestart + index;
                        base.SelectionLength = words[i].Length;
                        //base.SelectionFont  
                        base.SelectionColor = parser.IsKeyWord(words[i]);

                        base.SelectionStart = selectStart;
                        base.SelectionLength = 0;
                        //base.SelectionColor = Color.Black;
                    }


                }
                SendMessage(base.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
                base.Refresh();
            }
            base.OnTextChanged(e);
            //updateLabelRowIndex();
        }

        public new bool WordWrap
        {
            get { return base.WordWrap; }
            set { base.WordWrap = value; }
        }

        public enum Languages
        {
            SQL,
            VBSCRIPT,
            CSHARP,
            JSHARP
        }

        private Languages language = Languages.CSHARP;

        public Languages Language
        {
            get { return this.language; }
            set { this.language = value; }
        }

        private void richTextBoxMain_FontChanged(object sender, EventArgs e)
        {
            updateLabelRowIndex();
            richTextBoxMain_VScroll(null, null);
        }

        private void richTextBoxMain_Resize(object sender, EventArgs e)
        {
            richTextBoxMain_VScroll(null, null);
        }

        private void richTextBoxMain_VScroll(object sender, EventArgs e)
        {
            //move location of numberLabel for amount of pixels caused by scrollbar
            int p = base.GetPositionFromCharIndex(0).Y % (base.Font.Height + 1);
            base.Location = new Point(0, p);
            updateLabelRowIndex();
        }

        private void updateLabelRowIndex()
        {
            //we get index of first visible char and number of first visible line
            Point pos = new Point(0, 0);
            int firstIndex = this.GetCharIndexFromPosition(pos);
            int firstLine = this.GetLineFromCharIndex(firstIndex);

            //now we get index of last visible char and number of last visible line
            pos.X += this.ClientRectangle.Width;
            pos.Y += this.ClientRectangle.Height;
            int lastIndex = this.GetCharIndexFromPosition(pos);
            int lastLine = this.GetLineFromCharIndex(lastIndex);

            //this is point position of last visible char,
            //we'll use its Y value for calculating numberLabel size
            pos = this.GetPositionFromCharIndex(lastIndex);

            _LineId.Text = "";
            for (int i = firstLine; i <= lastLine + 1; i++)
            {
                _LineId.Text += i + 1 + "\r\n";
            }
        }  
    } 
}
